<?php
/**
 * Pert Library
 * Mysql Classes
 *
 * @author	Emre Hasegeli <hasegeli@gmail.com>
 * @license	GNU General Public License v3
 */

namespace Pert;

class MysqlConnection
{
	private $dbName, $host, $port, $user, $password;

	protected $connection;

	const persistentConnection = true;

	protected function __construct($dbName, $host = 'localhost', $port='3306', $user = 'root', $password = NULL)
	{
		$this->dbName= $dbName;

		$this->host= $host;

		$this->port= $port;

		$this->user= $user;

		$this->password= $password;
	}

	protected function __destruct()
	{
		if(!self::persistentConnection)
			mysql_close($this->connection);
	}

	public function __toString()
	{
		return $this->dbName;
	}

	protected function connect()
	{
		if(self::persistentConnection)
			$this->connection = mysql_pconnect($this->host . ':' . $this->port, $this->user, $this->password);
		else
			$this->connection = mysql_connect($this->host . ':' . $this->port, $this->user, $this->password);

		if(!$this->connection)
		{
			throw new MysqlException($this, 'Cannot connected to the database.');

			unset($this);
		}

		mysql_select_db($this->dbName, $this->connection);
	}
}

class Mysql extends MysqlConnection
{
	private $totalQueryCount, $totalQueryTime, $connectionTime;

	private static $id = 0;

	private static $mysqlConnection;

	public static $Debug = false;

	public function __construct($dbName, $host = 'localhost', $post = '5432', $user = 'root', $password = NULL)
	{
		parent::__construct($dbName, $host, $post, $user, $password);

		self::$mysqlConnection[$dbName] = $this;
		
		self::$mysqlConnection[self::$id++] = $this;
	}

	public function __destruct()
	{
		if(isset($this->connection))
			parent::__destruct();

		if(self::$Debug)
			echo $this->totalQueryTimeString();
	}

	protected function connect()
	{
		parent::connect();

		if(self::$Debug)
		{
			$this->totalQueryCount = 0;

			$this->totalQueryTime = 0;
		}
	}

	private $queryArray = array();

	private function checkQueryIfExecuted($query)
	{
		foreach($this->queryArray AS $q)
			if($q == $query)
				echo "<p >Same query: '" . $q . "'</p>";

		$this->queryArray[] = $query;
	}

	public function Query($query)
	{
		if(empty($this->connection))
			$this->connect();

		if(self::$Debug)
		{
			$this->checkQueryIfExecuted($query);

			$this->startTime();
		}

		if(!($result = mysql_query($query, $this->connection)))
			throw new MysqlException($this, mysql_error($this->connection));

		if(self::$Debug)
			$this->stopTime();

		return MysqlResult::Select($this, $result);
	}

	private function totalQueryTimeString()
	{
		if($this->totalQueryCount)
			return $this->totalQueryCount . ' Query executed in ' . $this->totalQueryTime . ' second.' . (self::$id > 1 ? ' (' . $this . ')' : '');
		else
			return '';
	}

	private function startTime()
	{
		$microTime = microtime();
		
		$microTime = explode(' ', $microTime);
		
		$microTime = $microTime[1] + $microTime[0];
		
		$this->connectionTime = $microTime;
	}

	private function stopTime()
	{
		$microTime = microtime();
		
		$microTime = explode(' ', $microTime);
		
		$microTime = $microTime[1] + $microTime[0];

		$this->totalQueryCount++;

		$this->totalQueryTime += ($microTime - $this->connectionTime);
	}

	public function Input($input)
	{
		if(is_bool($input))
			if($input)
				return 'true';
			else
				return 'false';
		elseif($input instanceof DateTime)
			return "'" . $input->format('Y-m-d') . "'";
		elseif(is_int($input))
			return $input;
		elseif(is_float($input))
			return $input;
		elseif(!$input)
			return 'NULL';
		else
		{
			if(empty($this->connection))
				$this->connect();
				
			return "'" . mysql_real_escape_string(get_magic_quotes_gpc() ? stripslashes($input) : $input, $this->connection) . "'";
		}
	}

	public static function Select($key = 0)
	{
		if(empty(self::$mysqlConnection[$key]))
			throw new MysqlException($self, 'No MysqlSql connection');

		return self::$mysqlConnection[$key];
	}
}

class MysqlException extends Exception
{
	private $mysql, $queryName;

	public function __construct(Mysql $mysql, $message, $queryName = NULL)
	{
		$this->mysql = $mysql;

		parent::__construct($message);

		$this->queryName = $queryName;
	}

	public function __toString()
	{
		$string = 'Database (' . $this->mysql . ') exception occured';

		if($this->queryName)
			$string .= " in '" . $this->queryName . "' query";

		$string .= ': ' . $this->message;

		return $string;
	}
}

class MysqlResult
{
	private $result;
	private $row;
	private $mysql;

	private $remainingRow;

	public function __construct(Mysql $mysql, $result)
	{
		$this->mysql = $mysql;
		$this->result = $result;

		$this->remainingRow = mysql_num_rows($this->result);
	}

	public function __toString()
	{
		if(!(isset($this->row)))
			$this->Row();

		return $this->row[0];
	}

	public function __get($column)
	{
		if($this->__isset($column))
			return $this->row[$column];
	}

	public function __isset($column)
	{
		if(!(isset($this->row)))
			$this->Row();

		return isset($this->row[$column]);
	}

	public function Row()
	{
		if($row = mysql_fetch_array($this->result))
		{
			$this->row = $row;

			$this->remainingRow--;

			return $this;
		}
	}

	public function RemainingRow()	
	{
		return $this->remainingRow;
	}

	public function FieldName($id)
	{
		return mysql_field_name($this->result, $id);
	}

	public static function Select(Mysql $mysql, $result)
	{
		if((is_resource($result)) and (mysql_num_rows($result)))
			return new self($mysql, $result);
	}
}
